package com.plian.system.service.fr.audit.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.metadata.fill.FillWrapper;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.plian.Tools.*;
import com.plian.Tools.pagedata.PageData;
import com.plian.system.bean.fr.AuditProjectExportBean;
import com.plian.system.common.status.CustomCode;
import com.plian.system.common.status.MyCustomCode;
import com.plian.system.constant.sys.FormStatusConstant;
import com.plian.system.constant.sys.FormTypeConstant;
import com.plian.system.dto.fr.audit.AuditReportDTO;
import com.plian.system.entity.fr.audit.AuditContact;
import com.plian.system.entity.fr.audit.AuditProject;
import com.plian.system.entity.fr.audit.AuditReport;
import com.plian.system.entity.fr.audit.AuditSystem;
import com.plian.system.mapper.fr.audit.AuditReportMapper;
import com.plian.system.model.CommonCode;
import com.plian.system.model.ObjResult;
import com.plian.system.mybatis.base.BaseFlowService;
import com.plian.system.mybatis.base.BaseServiceImpl;
import com.plian.system.mybatis.support.Condition;
import com.plian.system.service.fr.audit.IAuditContactService;
import com.plian.system.service.fr.audit.IAuditProjectService;
import com.plian.system.service.fr.audit.IAuditReportService;
import com.plian.system.service.fr.audit.IAuditSystemService;
import com.plian.system.service.sys.code.IFormCodeService;
import com.plian.system.service.sys.file.FileService;
import com.plian.system.service.wf.FormWorkflowService;
import com.plian.system.vo.fr.audit.*;
import com.plian.system.vo.report.HeaderVO;
import com.plian.system.vo.report.ReportReturnVO;
import com.plian.system.vo.report.ReportVO;
import com.plian.system.wrapper.fr.audit.AuditContactWrapper;
import com.plian.system.wrapper.fr.audit.AuditProjectWrapper;
import com.plian.system.wrapper.fr.audit.AuditReportWrapper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

import static com.plian.system.constant.sys.FormStatusConstant.UNREVIEWED;

/**
 * 内审报告备案管理 服务实现类
 *
 * @author gangan
 * @since 2019-09-30
 */
@Slf4j
@Service
@AllArgsConstructor
public class AuditReportServiceImpl extends BaseServiceImpl<AuditReportMapper, AuditReport> implements IAuditReportService, BaseFlowService {

    private AuditReportMapper auditReportMapper;

    private IAuditContactService auditContactService;

    private IAuditProjectService auditProjectService;

    private IAuditSystemService auditSystemService;

    private FormWorkflowService formWorkflowService;

    private IFormCodeService formCodeService;

    private FileService fileService;

    private Environment environment;

    private AuditContactWrapper auditContactWrapper;

    private AuditProjectWrapper auditProjectWrapper;

    private AuditReportWrapper auditReportWrapper;

    private static Map<String, String> fileNameMap = new HashMap<>();

    @Override
    public boolean saveOrUpdate(AuditReport auditReport) {
        boolean flag = false;
        if (auditReport.getId() == null){
            flag = true;
            auditReport.setVersion("1");
            auditReport.setCode(formCodeService.getCode(FormTypeConstant.FR_AUDITREPORT_FORM));
            auditReport.setFormType(String.valueOf(FormTypeConstant.FR_AUDITREPORT_FORM));
        }

        boolean result = super.saveOrUpdate(auditReport);
        if (flag){
            fileService.saveAll(String.valueOf(auditReport.getId()), auditReport.getDocList());
        }else {
            fileService.edit(String.valueOf(auditReport.getId()), auditReport.getDocList());
        }

        return result;
    }

    @Override
    public boolean deleteLogic(List<Long> ids) {
        for (Long id : ids) {
            AuditReport auditReport = getById(id);
            if (auditReport == null) {
                return false;
            } else if (auditReport.getStatus() != null &&
                    (auditReport.getStatus() != FormStatusConstant.STASH && auditReport.getStatus() != FormStatusConstant.UNREVIEWED)) {
                return false;
            }
            formWorkflowService.deleteProcessInstance(String.valueOf(id));
        }
        boolean result = super.deleteLogic(ids);
        if (result){
            auditContactService.deleteLogicByReportId(ids);
            auditProjectService.deleteLogicByReportId(ids);
            auditSystemService.deleteLogicByReportId(ids);
        }
        return result;
    }

    @Override
    public HashMap<String,Object> commit(String id) {
        handleStatus(Long.valueOf(id), FormStatusConstant.FLOW);

        //将需要提交的流程变量返回，用于工作流侧
        return new HashMap<String,Object>();
    }

    @Override
    public Boolean finish(String id) {
        /**
         * 设置审批人和审批时间
         */
        AuditReport auditReport = getById(id);
        auditReport.setApprovedUser(TokenUtil.getTokenUserId());
        Date now = DateUtil.now();
        auditReport.setApprovedTime(now);
        updateById(auditReport);

        return handleStatus(Long.valueOf(id), FormStatusConstant.FINISH);
    }

    @Override
    public Boolean reject(String id, int formState, String comment) {
        return handleStatus(Long.valueOf(id), formState);
    }

    @Override
    public ObjResult checkSubmit(AuditReport auditReport) {
        List<AuditContact> auditContacts = auditReport.getAuditContacts();
        if (CollectionUtils.isEmpty(auditContacts)){
            return new ObjResult(MyCustomCode.BILL_WRITE_PERSON);
        }

        if (!Optional.ofNullable(auditReport.getFillingYear()).isPresent()){
            return new ObjResult(CustomCode.builder().message("填报年份为空").code(1999).success(false).build());
        }

        if (!Optional.ofNullable(auditReport.getCompanyNameId()).isPresent()){
            return new ObjResult(CustomCode.builder().message("企业名称为空").code(1999).success(false).build());
        }

        AuditReport old = getOne(new LambdaQueryWrapper<AuditReport>()
                .eq(AuditReport::getFillingYear,auditReport.getFillingYear())
                .eq(AuditReport::getCompanyNameId,auditReport.getCompanyNameId()));


        if (Optional.ofNullable(old).isPresent() && !old.getId().equals(auditReport.getId())){
            return new ObjResult(CustomCode.builder().message("该年份已填报").code(1999).success(false).build());
        }

        return null;
    }

    @Override
    @Transactional(rollbackFor=RuntimeException.class)
    public Boolean submit(AuditReport auditReport) {
        if (!Optional.ofNullable(auditReport.getStatus()).isPresent()){
            auditReport.setStatus(FormStatusConstant.UNREVIEWED);
        }

        boolean result = saveOrUpdate(auditReport);

        if (result){
            List<AuditContact> auditContacts = auditReport.getAuditContacts();
            if (!CollectionUtils.isEmpty(auditContacts)){
                for (AuditContact auditContact : auditContacts){
                    if (auditContact.getId() != null &&
                            Optional.ofNullable(auditContact.getIsDeleted()).isPresent() && auditContact.getIsDeleted().intValue() == 1) {
                        auditContactService.removeById(auditContact.getId());
                    }else {
                        auditContact.setAuditReportId(auditReport.getId());
                        auditContactService.saveOrUpdate(auditContact);
                    }
                }
            }

            // 本年已审计项目
            List<AuditProject> auditProjects = auditReport.getAuditProjects();
            if (!CollectionUtils.isEmpty(auditProjects)){
                for (AuditProject auditProject : auditProjects){
                    if (auditProject.getId() != null &&
                            Optional.ofNullable(auditProject.getIsDeleted()).isPresent() && auditProject.getIsDeleted().intValue() == 1) {
                        auditProjectService.removeById(auditProject.getId());
                    }else {
                        auditProject.setAuditReportId(auditReport.getId());
                        auditProject.setIsPlan(1);
                        auditProjectService.saveOrUpdate(auditProject);
                    }
                }
            }

            // 计划审计项目
            List<AuditProject> planAuditProjects = auditReport.getPlanAuditProjects();
            if (!CollectionUtils.isEmpty(planAuditProjects)){
                for (AuditProject auditProject : planAuditProjects){
                    if (auditProject.getId() != null &&
                            Optional.ofNullable(auditProject.getIsDeleted()).isPresent() && auditProject.getIsDeleted().intValue() == 1) {
                        auditProjectService.removeById(auditProject.getId());
                    }else {
                        auditProject.setAuditReportId(auditReport.getId());
                        auditProject.setIsPlan(2);
                        auditProjectService.saveOrUpdate(auditProject);
                    }
                }
            }

            // 审计制度
            List<AuditSystem> auditSystems = auditReport.getAuditSystems();
            if (!CollectionUtils.isEmpty(auditSystems)){
                for (AuditSystem auditSystem : auditSystems){
                    if (auditSystem.getId() != null &&
                            Optional.ofNullable(auditSystem.getIsDeleted()).isPresent() && auditSystem.getIsDeleted().intValue() == 1) {
                        auditSystemService.removeById(auditSystem.getId());
                    }else {
                        auditSystem.setAuditReportId(auditReport.getId());
                        auditSystemService.saveOrUpdate(auditSystem);
                    }
                }
            }


        }


        HashMap<String, Object> processVariable = new HashMap<>();
        formWorkflowService.startProcessAndSimulate(String.valueOf(auditReport.getId()),auditReport.getFormType(),processVariable);

        return result;
    }

    @Override
    public ReportReturnVO auditStatistics(AuditReportDTO auditReportDTO){
        List<AuditReportStatisticsVO> statisticsVOS = getAuditStatistics(auditReportDTO);

        ReportReturnVO reportReturnVO = new ReportReturnVO();
        ReportVO reportVO = new ReportVO();
        reportVO
                .addHeader(HeaderVO.builder().key("companyName").title("单位名称").build())
                .addHeader(HeaderVO.builder().key("internalAuditAgency").title("内审机构名称").build())
                .addHeader(HeaderVO.builder().key("head").title("内审机构负责人").build())
                .addHeader(HeaderVO.builder().key("leader").title("内审工作分管领导").build())
                .addHeader(HeaderVO.builder().key("fullTimeStaff").title("专职").build())
                .addHeader(HeaderVO.builder().key("partTime").title("兼职").build())
                .addHeader(HeaderVO.builder().key("isDevelopSystem").title("是否制定内审制度").build())

                .addHeader(HeaderVO.builder().key("title1").title(auditReportDTO.getFillingYear() + "年审计单位(个)").build())
                .addHeader(HeaderVO.builder().key("sum1").title("合计").build())
                .addHeader(HeaderVO.builder().key("sum2").title("财务收支审计").build())
                .addHeader(HeaderVO.builder().key("sum3").title("经济责任审计").build())
                .addHeader(HeaderVO.builder().key("sum4").title("基本建设审计").build())
                .addHeader(HeaderVO.builder().key("sum5").title("投资后评价审计").build())
                .addHeader(HeaderVO.builder().key("sum6").title("内控审计").build())
                .addHeader(HeaderVO.builder().key("sum7").title("财务决算审计").build())
                .addHeader(HeaderVO.builder().key("sum8").title("绩效审计").build())
                .addHeader(HeaderVO.builder().key("sum9").title("其他专项审计").build())

                .addHeader(HeaderVO.builder().key("title2").title(Integer.valueOf(auditReportDTO.getFillingYear()) + 1 + "年审计项目计划(个)").build())
                .addHeader(HeaderVO.builder().key("planSum1").title("合计").build())
                .addHeader(HeaderVO.builder().key("planSum2").title("财务收支审计").build())
                .addHeader(HeaderVO.builder().key("planSum3").title("经济责任审计").build())
                .addHeader(HeaderVO.builder().key("planSum4").title("基本建设审计").build())
                .addHeader(HeaderVO.builder().key("planSum5").title("投资后评价审计").build())
                .addHeader(HeaderVO.builder().key("planSum6").title("内控审计").build())
                .addHeader(HeaderVO.builder().key("planSum7").title("财务决算审计").build())
                .addHeader(HeaderVO.builder().key("planSum8").title("绩效审计").build())
                .addHeader(HeaderVO.builder().key("planSum9").title("其他专项审计").build())
                .addHeader(HeaderVO.builder().key("sum1").title(auditReportDTO.getFillingYear() + "年实际备案报告数").build());

        reportVO.setData(statisticsVOS);
        reportReturnVO.setGrids(reportVO);
        return reportReturnVO;
    }

    @Override
    public String exportAuditStatistics(AuditReportDTO auditReportDTO) {

        String key = System.currentTimeMillis() + StringPool.DASH + UUID.randomUUID().toString().replace(StringPool.DASH, "");
        String fileName = key + ".xlsx";
        String exportPath = environment.getProperty("excel.path");

        /**
         * 导出路径不存在则创建
         */
        File file = new File(exportPath);
        if (!file.exists()) {
            file.mkdirs();
        }
        InputStream inputStream = this.getClass().getResourceAsStream("/exceldemo/auditStatistics.xlsx");
        ExcelWriter excelWriter = EasyExcel.write(exportPath + fileName).withTemplate(inputStream).build();

        // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行，然后下面的数据往后移动。默认 是false，会直接使用下一行，如果没有则创建。
        // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了，所以慎用
        // 简单的说 如果你的模板有list,且list不是最后一行，下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存

        FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();

        // 表一
        WriteSheet writeSheet = EasyExcel.writerSheet(0).build();
        List<AuditReportStatisticsVO> statisticsVOS = getAuditStatistics(auditReportDTO);
        excelWriter.fill(new FillWrapper("statisticsVOS", statisticsVOS), fillConfig, writeSheet);

        // 其他参数可以使用Map封装
        Map<String, Object> otherMap = new HashMap<>(16);
        otherMap.put("nextYear", Integer.valueOf(auditReportDTO.getFillingYear()) + 1);
        otherMap.put("year", auditReportDTO.getFillingYear());

        excelWriter.fill(otherMap, writeSheet);
        excelWriter.finish();
        fileNameMap.put(key, auditReportDTO.getFillingYear() + "年度邢台市属国有企业内部审计工作情况统计表");
        return key;
    }

    @Override
    public String exportAuditContactStatistics(AuditReportDTO auditReportDTO) {
        String key = System.currentTimeMillis() + StringPool.DASH + UUID.randomUUID().toString().replace(StringPool.DASH, "");
        String fileName = key + ".xlsx";
        String exportPath = environment.getProperty("excel.path");

        /**
         * 导出路径不存在则创建
         */
        File file = new File(exportPath);
        if (!file.exists()) {
            file.mkdirs();
        }
        InputStream inputStream = this.getClass().getResourceAsStream("/exceldemo/auditContactStatistics.xlsx");
        ExcelWriter excelWriter = EasyExcel.write(exportPath + fileName).withTemplate(inputStream).build();

        // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行，然后下面的数据往后移动。默认 是false，会直接使用下一行，如果没有则创建。
        // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了，所以慎用
        // 简单的说 如果你的模板有list,且list不是最后一行，下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存

        FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();

        // 表一
        WriteSheet writeSheet = EasyExcel.writerSheet(0).build();
        List<AuditContactStatisticsVO> auditContactStatisticsVOS = auditContactService.getExportAuditContactStatistics(auditReportDTO);
        excelWriter.fill(new FillWrapper("auditContactStatisticsVOS", auditContactStatisticsVOS), fillConfig, writeSheet);

        excelWriter.finish();
        fileNameMap.put(key, auditReportDTO.getFillingYear() + "年度宁波市属企业主要内审工作领导和人员联系方式");
        return key;
    }

    @Override
    public String exportAuditSystemStatistics(AuditReportDTO auditReportDTO) {
        String key = System.currentTimeMillis() + StringPool.DASH + UUID.randomUUID().toString().replace(StringPool.DASH, "");
        String fileName = key + ".xlsx";
        String exportPath = environment.getProperty("excel.path");

        /**
         * 导出路径不存在则创建
         */
        File file = new File(exportPath);
        if (!file.exists()) {
            file.mkdirs();
        }
        InputStream inputStream = this.getClass().getResourceAsStream("/exceldemo/auditSystemStatistics.xlsx");
        ExcelWriter excelWriter = EasyExcel.write(exportPath + fileName).withTemplate(inputStream).build();

        // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行，然后下面的数据往后移动。默认 是false，会直接使用下一行，如果没有则创建。
        // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了，所以慎用
        // 简单的说 如果你的模板有list,且list不是最后一行，下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存

        FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();

        // 表一
        WriteSheet writeSheet = EasyExcel.writerSheet(0).build();
        List<AuditSystemStatisticsVO> auditSystemStatisticsVOS = auditSystemService.getExportAuditSystemStatistics(auditReportDTO);
        excelWriter.fill(new FillWrapper("auditSystemStatisticsVOS", auditSystemStatisticsVOS), fillConfig, writeSheet);

        excelWriter.finish();
        fileNameMap.put(key, auditReportDTO.getFillingYear() + "年度内部审计相关制度统计");
        return key;
    }

    @Override
    public String export(Long auditReportId) {

        AuditReportVO auditReportVO = auditReportWrapper.entityToVO(getById(auditReportId));
        if (!Optional.ofNullable(auditReportVO).isPresent()){
            //id不存在
            return "";
        }

        String key = System.currentTimeMillis() + StringPool.DASH + UUID.randomUUID().toString().replace(StringPool.DASH, "");
        String fileName = key + ".xlsx";
        String exportPath = environment.getProperty("excel.path");

        /**
        * 导出路径不存在则创建
        */
        File file = new File(exportPath);
        if (!file.exists()) {
        file.mkdirs();
        }
        InputStream inputStream = this.getClass().getResourceAsStream("/exceldemo/auditReport.xlsx");
        ExcelWriter excelWriter = EasyExcel.write(exportPath + fileName).withTemplate(inputStream).build();

        // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行，然后下面的数据往后移动。默认 是false，会直接使用下一行，如果没有则创建。
        // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了，所以慎用
        // 简单的说 如果你的模板有list,且list不是最后一行，下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存

        FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();

        // 表一
        WriteSheet writeSheet = EasyExcel.writerSheet(0).build();

        List<AuditContactVO> auditContacts = auditContactWrapper.entityToVO(
                auditContactService.list(new LambdaQueryWrapper<AuditContact>()
                        .eq(AuditContact::getAuditReportId,auditReportId)));

        Map<String, Object> baseMap = new HashMap<>(16);
        baseMap.put("companyName", auditReportVO.getCompanyName());
        baseMap.put("year", auditReportVO.getFillingYear());
        baseMap.put("nextYear", Integer.valueOf(auditReportVO.getFillingYear()) + 1);
        baseMap.put("internalAuditAgency", auditReportVO.getInternalAuditAgency());
        baseMap.put("internalAuditStaff", auditReportVO.getInternalAuditStaff());
        baseMap.put("fullTimeStaff", auditReportVO.getFullTimeStaff());

        if (CollectionUtil.isNotEmpty(auditContacts)){

            StringBuilder leaderName = new StringBuilder();
            StringBuilder leaderNumber = new StringBuilder();
            StringBuilder headName = new StringBuilder();
            StringBuilder headNumber = new StringBuilder();
            StringBuilder liaisonName = new StringBuilder();
            StringBuilder liaisonNumber = new StringBuilder();
            for (AuditContact auditContact : auditContacts){
                if ("042001".equals(auditContact.getProjectId())){
                    if ("".equals(leaderName)){
                        leaderName.append(StringPool.COMMA);
                        leaderNumber.append(StringPool.COMMA);
                    }
                    if (StringUtil.isNotBlank(auditContact.getName())){
                        leaderName.append(auditContact.getName());
                    }
                    if (StringUtil.isNotBlank(auditContact.getContactNumber())){
                       leaderNumber.append(auditContact.getContactNumber());
                    }

                }
                if ("042002".equals(auditContact.getProjectId())){
                    if ("".equals(headName)){
                        headName.append(StringPool.COMMA);
                        headNumber.append(StringPool.COMMA);
                    }
                    if (StringUtil.isNotBlank(auditContact.getName())){
                        headName.append(auditContact.getName());
                    }
                    if (StringUtil.isNotBlank(auditContact.getContactNumber())){
                        headNumber.append(auditContact.getContactNumber());
                    }
                }
                if ("042003".equals(auditContact.getProjectId())){
                    if ("".equals(liaisonName)){
                        liaisonName.append(StringPool.COMMA);
                        liaisonNumber.append(StringPool.COMMA);
                    }
                    if (StringUtil.isNotBlank(auditContact.getName())){
                        liaisonName.append(auditContact.getName());
                    }
                    if (StringUtil.isNotBlank(auditContact.getContactNumber())){
                        liaisonNumber.append(auditContact.getContactNumber());
                    }
                }
            }
            baseMap.put("leaderName", leaderName.length() > 0 ? leaderName.toString() : "");
            baseMap.put("leaderNumber", leaderNumber.length() > 0 ? leaderNumber.toString() : "");
            baseMap.put("headName",  headName.length() > 0 ? headName.toString() : "");
            baseMap.put("headNumber",  headNumber.length() > 0 ? headNumber.toString() : "");
            baseMap.put("liaisonName",  liaisonName.length() > 0 ? liaisonName.toString() : "");
            baseMap.put("liaisonNumber",  liaisonNumber.length() > 0 ? liaisonNumber.toString() : "");
        }
        excelWriter.fill(baseMap, writeSheet);

        //已审计项目清单
        List<AuditProjectVO> auditProjects = auditProjectWrapper.entityToVO(
                auditProjectService.list(new LambdaQueryWrapper<AuditProject>()
                        .eq(AuditProject::getIsPlan, 1)
                        .eq(AuditProject::getAuditReportId, auditReportId)));

        //计划审计清单
        List<AuditProjectVO> planAuditProjects = auditProjectWrapper.entityToVO(
                auditProjectService.list(new LambdaQueryWrapper<AuditProject>()
                        .eq(AuditProject::getIsPlan, 2)
                        .eq(AuditProject::getAuditReportId, auditReportId)));
        int maxSize = 0;
        if (CollectionUtil.isNotEmpty(auditProjects)){
            maxSize = auditProjects.size();
        }
        if (CollectionUtil.isNotEmpty(planAuditProjects) && planAuditProjects.size()>maxSize){
            maxSize = planAuditProjects.size();
        }
        List<AuditProjectExportBean> auditProjectExportBeans = new ArrayList<>();
        for (int i = 0; i < maxSize; i++){
            AuditProjectExportBean auditProjectExportBean = new AuditProjectExportBean();
            if (CollectionUtil.isNotEmpty(auditProjects) && i < auditProjects.size()){
                AuditProjectVO auditProjectVO = auditProjects.get(i);
                auditProjectExportBean.setAuditProjectName(auditProjectVO.getAuditProjectName());
                auditProjectExportBean.setAuditCategoryName(auditProjectVO.getAuditCategoryName());
                auditProjectExportBean.setAmount(auditProjectVO.getAmount());
            }

            if (CollectionUtil.isNotEmpty(planAuditProjects) && i < planAuditProjects.size()){
                AuditProjectVO planAuditProjectVO = planAuditProjects.get(i);
                auditProjectExportBean.setPlanAuditProjectName(planAuditProjectVO.getAuditProjectName());
                auditProjectExportBean.setPlanAmountStr(planAuditProjectVO.getAuditCategoryName() + "(" + planAuditProjectVO.getAmount() + ")");
            }
            auditProjectExportBeans.add(auditProjectExportBean);
        }

        excelWriter.fill(new FillWrapper("auditProjects", auditProjectExportBeans), fillConfig, writeSheet);

        excelWriter.finish();
        fileNameMap.put(key, auditReportVO.getFillingYear() + "年度" + auditReportVO.getCompanyName() +"内部审计工作情况表");
        return key;

    }

    private List<AuditReportStatisticsVO> getAuditStatistics(AuditReportDTO auditReportDTO){
        if (StringUtil.isBlank(auditReportDTO.getFillingYear())){
            auditReportDTO.setFillingYear(DateUtil.format(DateUtil.now(), DateUtil.PATTERN_YEAR));
        }

        List<AuditReportStatisticsVO> statisticsVOS = auditReportMapper.auditStatistics(auditReportDTO);
        if (!CollectionUtils.isEmpty(statisticsVOS)){
            List<AuditContact> auditContacts = auditContactService.list(
                    new LambdaQueryWrapper<AuditContact>()
                    .in(AuditContact::getAuditReportId, statisticsVOS.stream().map(AuditReportStatisticsVO::getId).collect(Collectors.toList())));
            Map<Long, List<AuditContact>> map = auditContacts.stream().collect(Collectors.groupingBy(AuditContact::getAuditReportId));

            AuditReportStatisticsVO end = AuditReportStatisticsVO.builder().index(statisticsVOS.size() + 1).companyName("合计").build();
            for (int i = 0; i < statisticsVOS.size(); i++){
                AuditReportStatisticsVO statisticsVO = statisticsVOS.get(i);
                statisticsVO.setIndex(i + 1);
                if (Optional.ofNullable(statisticsVO.getIsDevelopSystem()).isPresent()){
                    statisticsVO.setIsDevelopSystemStr(statisticsVO.getIsDevelopSystem() == 1 ? "否" : "是" );
                }

                end.setFullTimeStaff(BigDecimalUtil.integerAdd(end.getFullTimeStaff(), statisticsVO.getFullTimeStaff()));
                end.setPartTime(BigDecimalUtil.integerAdd(end.getPartTime(), statisticsVO.getPartTime()));
                end.setSum1(BigDecimalUtil.integerAdd(end.getSum1(), statisticsVO.getSum1()));
                end.setSum2(BigDecimalUtil.integerAdd(end.getSum2(), statisticsVO.getSum2()));
                end.setSum3(BigDecimalUtil.integerAdd(end.getSum3(), statisticsVO.getSum3()));
                end.setSum4(BigDecimalUtil.integerAdd(end.getSum4(), statisticsVO.getSum4()));
                end.setSum5(BigDecimalUtil.integerAdd(end.getSum5(), statisticsVO.getSum5()));
                end.setSum6(BigDecimalUtil.integerAdd(end.getSum6(), statisticsVO.getSum6()));
                end.setSum7(BigDecimalUtil.integerAdd(end.getSum7(), statisticsVO.getSum7()));
                end.setSum8(BigDecimalUtil.integerAdd(end.getSum8(), statisticsVO.getSum8()));
                end.setSum9(BigDecimalUtil.integerAdd(end.getSum9(), statisticsVO.getSum9()));
                end.setPlanSum1(BigDecimalUtil.integerAdd(end.getPlanSum1(), statisticsVO.getPlanSum1()));
                end.setPlanSum2(BigDecimalUtil.integerAdd(end.getPlanSum2(), statisticsVO.getPlanSum2()));
                end.setPlanSum3(BigDecimalUtil.integerAdd(end.getPlanSum3(), statisticsVO.getPlanSum3()));
                end.setPlanSum4(BigDecimalUtil.integerAdd(end.getPlanSum4(), statisticsVO.getPlanSum4()));
                end.setPlanSum5(BigDecimalUtil.integerAdd(end.getPlanSum5(), statisticsVO.getPlanSum5()));
                end.setPlanSum6(BigDecimalUtil.integerAdd(end.getPlanSum6(), statisticsVO.getPlanSum6()));
                end.setPlanSum7(BigDecimalUtil.integerAdd(end.getPlanSum7(), statisticsVO.getPlanSum7()));
                end.setPlanSum8(BigDecimalUtil.integerAdd(end.getPlanSum8(), statisticsVO.getPlanSum8()));
                end.setPlanSum9(BigDecimalUtil.integerAdd(end.getPlanSum9(), statisticsVO.getPlanSum9()));

                // 联系人数据处理
                List<AuditContact> contacts = map.get(statisticsVO.getId());
                if (CollectionUtil.isNotEmpty(contacts)){
                    for (AuditContact auditContact : contacts){
                        if ("042002".equals(auditContact.getProjectId())){
                            statisticsVO.setHead(auditContact.getName());
                            continue;
                        }
                        if ("042001".equals(auditContact.getProjectId())){
                            statisticsVO.setLeader(auditContact.getName());
                            continue;
                        }
                    }
                }
            }

            statisticsVOS.add(end);
        }
        return statisticsVOS;
    }

    /**
     * 统一处理状态机事件方法
     * @param id 主键
     * @param updateStatus 更新状态
     * @return boolean
     */
    private Boolean handleStatus(Long id, int updateStatus) {
        try {
            AuditReport auditReport = getById(id);
            auditReport.setStatus(updateStatus);
            updateById(auditReport);
            return true;
        } catch (Exception e) {
            log.error(ExceptionUtils.getFullStackTrace(e));
            return false;
        }
    }

    @Override
    public List<PageData> findByIds(List<String> formIds) {
        List<AuditReport> auditReports = list(new LambdaQueryWrapper<AuditReport>().in(AuditReport::getId, Func.toLongList(StringUtils.join(formIds, StringPool.COMMA))));
        List<AuditReportVO> auditReportVOS = auditReportWrapper.entityToVO(auditReports);
        List<PageData> pageDataList = new ArrayList<>();
        try{
            if (CollectionUtil.isNotEmpty(auditReportVOS)){
                for (AuditReportVO vo : auditReportVOS){
                    PageData pd = new PageData();
                    pd.put("id", vo.getId());
                    pd.put("UNITNAME", vo.getCompanyName());
                    pd.put("APPLICANTNAME", vo.getCreateUserName());
                    pd.put("applicantId", vo.getCreateUser());
                    pd.put("applyOrganizationId", vo.getCompanyNameId());
                    pd.put("formType", FormTypeConstant.FR_AUDITREPORT_FORM);
                    pd.put("orgName", vo.getCompanyName());
                    pageDataList.add(pd);
                }
            }

            return pageDataList;
        } catch (Exception e) {
            log.error(ExceptionUtils.getFullStackTrace(e));
        }
        return pageDataList;
    }

    @Override
    public List<Integer> getFormType() {
        return Arrays.asList(FormTypeConstant.FR_AUDITREPORT_FORM);
    }

    @Override
    public void goBackToStash(String formId) {
        handleStatus(Long.valueOf(formId), UNREVIEWED);
    }

    @Override
    public ObjResult getDetailObj(AuditReportVO auditReportVO) {
        ObjResult objResult;
        try {

            objResult = new ObjResult(CommonCode.SUCCESS);
            PageData pd = new PageData();
            if (Optional.ofNullable(auditReportVO).isPresent()) {
                pd.put("auditContacts", auditContactWrapper.entityToVO(
                        auditContactService.list(
                                new LambdaQueryWrapper<AuditContact>()
                                .eq(AuditContact::getAuditReportId, auditReportVO.getId()))
                ));

                pd.put("auditProjects", auditProjectWrapper.entityToVO(
                        auditProjectService.list(new LambdaQueryWrapper<AuditProject>()
                                .eq(AuditProject::getIsPlan, 1)
                                .eq(AuditProject::getAuditReportId, auditReportVO.getId()))
                ));

                pd.put("planAuditProjects", auditProjectWrapper.entityToVO(
                        auditProjectService.list(new LambdaQueryWrapper<AuditProject>()
                                .eq(AuditProject::getIsPlan, 2)
                                .eq(AuditProject::getAuditReportId, auditReportVO.getId()))
                ));

                pd.put("auditSystems", auditSystemService.list(new LambdaQueryWrapper<AuditSystem>()
                                .eq(AuditSystem::getAuditReportId, auditReportVO.getId()))
                );

                ObjectMapper mapper = new ObjectMapper();
                String json = JSON.toJSONStringWithDateFormat(auditReportVO, DateUtil.PATTERN_DATETIME);
                pd.putAll(mapper.readValue(json, new TypeReference<HashMap<String, String>>(){}));

                objResult.setPageData(pd);
            }
        } catch (Exception e) {
            log.error(ExceptionUtils.getFullStackTrace(e));
            objResult = new ObjResult(CommonCode.FAIL);
        }
        return objResult;
    }

    @Override
    public void download(String key, HttpServletResponse response) {
        if (!fileNameMap.containsKey(key)){
            log.error("文件下载失败！key=" + key);
        }

        String excelName = fileNameMap.get(key);
        String excelPath = environment.getProperty("excel.path") + "/" + key + ".xlsx";
        FileTools.exportExcel(excelPath, excelName, response);
        fileNameMap.remove(key);
    }

    @Override
    public ObjResult change(AuditReport auditReport) {
        ObjResult result = checkSubmit(auditReport);
        if (Optional.ofNullable(result).isPresent()){
            return result;
        }

        AuditReport old = getById(auditReport.getId());
        AuditContact auditContact = new AuditContact();
        auditContact.setAuditReportId(old.getId());
        old.setAuditContacts(auditContactService.list(Condition.getQueryWrapper(auditContact)));

        // @TODO 需要工作流支持
//        snapshotClient.push("auditReport", old.getId(), jsonObject);
        //auditReport.setStatus(BillStatus.INIT.billStatus());
        auditReport.setVersion(StringUtil.isBlank(auditReport.getVersion()) ?
                "1" : String.valueOf(Integer.valueOf(auditReport.getVersion())+1));

        return submit(auditReport)? new ObjResult(CommonCode.SUCCESS) : new ObjResult(CommonCode.FAIL);
    }
}
